STACK LORAWAN
00 · Resumen ejecutivo

Un stack para supervisar contadores de energía por radio, sin cableado de datos.

Arquitectura completa para leer contadores eléctricos Modbus (Eastron SDM630) a través de LoRaWAN y volcarlos en un panel de control con histórico y alarmas: Contador → Dragino RS485-LN → Gateway LoRaWAN → ChirpStack → Mosquitto → Node-RED → ThingsBoard CE. Pensada para 20-50 contadores en un radio de hasta 5 km, en la región europea EU868, desplegada con docker compose en un único servidor Ubuntu.

Del registro Modbus del contador a un dashboard en el navegador, sin ningún cable de datos entre medias.

Cada contador se conecta a un nodo Dragino que traduce sus lecturas Modbus a radio LoRa. El gateway capta esa radio y la reenvía al servidor, donde cuatro piezas de software encadenadas —ChirpStack, Mosquitto, Node-RED y ThingsBoard— descifran, traducen y visualizan el dato hasta convertirlo en un gráfico en tiempo real.

EU868 · Clase A
región y clase LoRaWAN de los nodos Dragino
20-50 contadores
radio de hasta 5 km con línea de vista razonable
4 servicios + BD
ChirpStack, Mosquitto, Node-RED, ThingsBoard CE
docker compose
stack completo en un único servidor Ubuntu
Cómo está organizada esta página

01-03 · Paneles de acceso

ChirpStack, ThingsBoard y Node-RED, cada uno en su propia pestaña, embebidos con la URL que tú indiques.

04 · Arquitectura

Qué hace cada pieza del stack, en una frase, y cómo se conectan entre sí.

05 · Flujo de un dato

El recorrido completo de una lectura de tensión, paso a paso, desde el contador hasta la pantalla.

06-08 · Despliegue

Puesta en marcha, el codec de decodificación del SDM630, y qué reforzar antes de producción.

01 · ChirpStack

Network & Application Server LoRaWAN, embebido aquí.

Autentica gateways y dispositivos, descifra las tramas, ejecuta el codec de cada Device Profile y gestiona applications, gateways y devices. Por defecto apunta a http://localhost:8080, el puerto que publica el docker-compose.yml del stack — cambia la URL si tu instancia vive en otra máquina, dominio o detrás de un reverse proxy.

admin / admin (cámbialas en el primer login) · la URL se recuerda en este navegador
↗ Abrir en pestaña nueva

Si no carga: comprueba que el contenedor está arriba (docker compose ps) y que la URL es alcanzable desde este navegador — "Abrir en pestaña nueva" es el diagnóstico más rápido.

02 · ThingsBoard CE

Dashboards, alarmas e histórico, embebidos aquí.

Visualización en tiempo real, gestión de usuarios y clientes, reglas de alarma y almacenamiento de series temporales de cada contador. Por defecto apunta a http://localhost:8081 (el docker-compose.yml mapea el 9090 interno de ThingsBoard a ese puerto del host) — cámbiala si tu instancia vive en otra máquina o dominio.

sysadmin@thingsboard.org / sysadmin (cámbialas en el primer login) · la URL se recuerda en este navegador
↗ Abrir en pestaña nueva

Si no carga: comprueba que el contenedor está arriba (docker compose ps) y que la URL es alcanzable desde este navegador — "Abrir en pestaña nueva" es el diagnóstico más rápido.

03 · Node-RED

El traductor ChirpStack → ThingsBoard, embebido aquí.

Editor de flujos donde se importa flows-example.json: escucha los uplinks decodificados en Mosquitto y los reempaqueta en la Gateway API de ThingsBoard. Por defecto apunta a http://localhost:1880 — cámbiala si tu instancia vive en otra máquina o dominio.

sin autenticación por defecto — protégelo si el editor queda expuesto · la URL se recuerda en este navegador
↗ Abrir en pestaña nueva

Si no carga: comprueba que el contenedor está arriba (docker compose ps) y que la URL es alcanzable desde este navegador — "Abrir en pestaña nueva" es el diagnóstico más rápido.

04 · Arquitectura

Ocho piezas, cada una con un único trabajo.

El punto que más suele confundir: ChirpStack y ThingsBoard no se hablan directamente. Cada uno tiene su propio "idioma" de MQTT (topics y formato de payload distintos), por eso Node-RED existe en medio — sin él, los datos llegarían a Mosquitto pero ThingsBoard no sabría interpretarlos.

Diagrama de flujo completo
⚡

Contador SDM630

Modbus RTU

→
📡

Dragino RS485-LN

RS485 → LoRaWAN

→
📶

Gateway LoRaWAN

radio EU868

→
🔌

gateway-bridge

UDP 1700 → MQTT

→
🧠

ChirpStack

Network Server LoRaWAN

→
📨

Mosquitto

broker MQTT central

→
🔀

Node-RED

traductor de formato

→
📊

ThingsBoard CE

dashboards y alarmas

ChirpStack lee y escribe su propio estado (claves, sesiones, dispositivos) en PostgreSQL + Redis, en paralelo al flujo de datos — no forma parte de la cadena de mensajes, es su memoria interna.

Qué hace cada pieza, en una frase
ComponenteRolAnalogía
Contador SDM630Mide la energía y expone sus datos por Modbus RTUEl "sensor"
Dragino RS485-LNTraduce Modbus → radio LoRaEl "módem" del contador
Gateway LoRaWANAntena que capta la radio y la reenvía por IPLa "antena de telefonía"
gateway-bridgeTraduce el protocolo UDP del gateway a MQTTUn adaptador de enchufe
ChirpStackAutentica, descifra y decodifica cada trama (aplica el codec)El "operador de telefonía"
Postgres / RedisGuardan el estado interno de ChirpStack (claves, sesiones...)La "base de datos" del operador
MosquittoAutopista de mensajes MQTT compartida por todosEl "cartero"
Node-REDReformatea el mensaje de ChirpStack al formato de ThingsBoardEl "traductor"
ThingsBoard CEDashboards, alarmas, histórico, usuarios/clientesEl panel de control del usuario final
Piezas sin interfaz web

A diferencia de ChirpStack, ThingsBoard y Node-RED (sección 01-03), estas dos piezas no tienen panel embebible: son infraestructura de transporte, no interfaces de usuario.

05 · Flujo de un dato real

De "230 V en la fase L1" a un punto en una gráfica, paso a paso.

El mismo recorrido, pero contado como la vida de un único dato — útil para depurar el stack cuando algo no llega hasta el dashboard.

1 · El contador mide

El contador mide, por ejemplo, 230 V en la fase L1, junto con el resto de registros Modbus configurados (corrientes, potencias, energía acumulada).

2 · El Dragino empaqueta

El Dragino RS485-LN lee ese registro Modbus por su bus RS485 y lo mete en una trama de radio LoRa, junto con las demás lecturas configuradas por AT+COMMAND.

3 · El gateway reenvía

El gateway LoRaWAN capta la señal de radio y la reenvía por Internet/Ethernet al servidor, mediante UDP (protocolo Semtech packet forwarder), al puerto 1700.

4 · El bridge traduce el transporte

chirpstack-gateway-bridge convierte ese paquete UDP en un mensaje MQTT interno, publicado en Mosquitto.

5 · ChirpStack decodifica

ChirpStack descifra la trama, identifica el dispositivo Dragino, ejecuta el codec sdm630_codec.js de su Device Profile y obtiene {"voltaje_l1": 230.4, ...}.

6 · ChirpStack publica

ChirpStack publica ese JSON ya decodificado en Mosquitto, en el topic application/.../event/up.

7 · Node-RED reempaqueta

Node-RED está escuchando ese topic, coge el JSON y lo reempaqueta en el formato que espera ThingsBoard (v1/gateway/telemetry, Gateway API).

8 · ThingsBoard visualiza

ThingsBoard recibe el dato, lo guarda en su base de datos de series temporales y lo muestra en el dashboard en tiempo real — o dispara una alarma si supera un umbral.

06 · Puesta en marcha

De un Ubuntu recién instalado a los primeros datos en el dashboard.

Resumen de los pasos del stack; el docker-compose.yml completo, con nueve servicios (Postgres, Redis, Mosquitto, gateway-bridge, ChirpStack, Node-RED y ThingsBoard) y sus volúmenes, vive junto al resto de ficheros de configuración del proyecto.

0 · Requisitos

Docker + Compose

Ubuntu Server con docker.io y el plugin docker-compose-plugin instalados.

Gateway LoRaWAN físico

RAK7248/7249, MikroTik wAP LR8/LR9, Dragino LPS8... con línea de vista razonable a los puntos de medición.

Un Dragino RS485-LN por bus

Un nodo por cada bus RS485 (Modbus permite varios SDM630 en el mismo bus, así que un cuadro con varios contadores puede compartir nodo).

Arranque automático (recomendado)

Este script crea todo el árbol de directorios y archivos de configuración del stack —idéntico a los pasos 1 y 2 de abajo, con una contraseña de PostgreSQL y un secreto de API generados al azar— y lo levanta con docker compose up -d. Necesita Docker y docker-compose-plugin ya instalados (paso 0).

# dale permisos de ejecución y ejecútalo (crea ./energy-lora-stack por defecto)
chmod +x bootstrap-energy-lora-stack.sh
./bootstrap-energy-lora-stack.sh [directorio-destino]

El script solo automatiza los pasos 1 y 2 (entorno + arranque del stack). Los pasos 3 a 7 (dar de alta el gateway físico, los contadores y el flujo de Node-RED) siguen siendo manuales: dependen de hardware y credenciales que solo tú tienes.

1 · Preparar el entorno — ya hecho si usaste el script
# copia la plantilla de variables de entorno
cp .env.example .env
nano .env   # cambia CHIRPSTACK_PG_PASSWORD

# la misma password debe reflejarse en:
#   configuration/chirpstack/chirpstack.toml -> [postgresql] dsn

Cambia también el secret de chirpstack.toml por una cadena aleatoria larga: openssl rand -base64 32.

2 · Levantar el stack — ya hecho si usaste el script
docker compose up -d
docker compose ps        # comprueba que todo está "healthy"/"up"
docker compose logs -f chirpstack   # por si hay que depurar algo
3-7 · Configurar el resto de la cadena

3 · Gateway físico

Configura el packet forwarder Semtech UDP del gateway apuntando a la IP del servidor, puerto 1700. Luego dalo de alta en ChirpStack (Gateways → Add gateway) con su Gateway ID (EUI).

4 · Contadores en ChirpStack

Crea una Application, un Device Profile EU868/Clase A con el codec del SDM630 en la pestaña Codec, y da de alta cada contador como Device con su DevEUI/AppKey.

5 · Nodo Dragino RS485-LN

Configura el modo Modbus (baudrate, paridad) y los registros del SDM630 a leer, verificando el datasheet exacto de tu variante/firmware.

6 · Gateway en ThingsBoard

Crea un Device de tipo "Gateway", copia su Access Token y pégalo en el nodo broker-thingsboard del flujo de Node-RED.

7 · Importar el flujo en Node-RED

Abre el editor de Node-RED (panel de acceso, sección 03), importa node-red/flows-example.json, añade el Access Token de ThingsBoard en el nodo "ThingsBoard (Gateway API)" y despliega. Cada contador aparecerá como dispositivo hijo del gateway la primera vez que llegue una trama.

Servicios y puertos expuestos
ServicioPuerto hostCredenciales por defecto
ChirpStack (web)8080/tcpadmin / admin
ThingsBoard (web)8081/tcpsysadmin@thingsboard.org / sysadmin
Node-RED (editor)1880/tcpsin autenticación por defecto
Mosquitto (MQTT)1883/tcpallow_anonymous true por defecto
gateway-bridge (Semtech UDP)1700/udp— (aquí apunta el gateway físico)

Alcance de 5 km con LoRa: en EU868 con línea de vista razonable y el gateway en un punto alto, 5 km es alcanzable; en entorno urbano con obstáculos conviene una prueba de campo con un solo nodo antes de desplegar los 20-50 definitivos. Los intervalos de envío largos (varios minutos) permiten usar Spreading Factors altos (SF10-SF12): más alcance a costa de más tiempo en el aire, perfecto para supervisión energética sin necesidad de telemetría segundo a segundo.

07 · El codec del contador

De bytes crudos LoRaWAN a un JSON con voltajes, corrientes y potencia.

El Dragino RS485-LN concatena en el payload el resultado de cada AT+COMMAND configurado, en el mismo orden. ChirpStack ejecuta esta función decodeUplink (Device Profile → pestaña Codec) para convertir esos bytes en campos con nombre, antes de publicarlos en Mosquitto.

Formato de cada campo

8 registros float IEEE754 de 32 bits, big-endian ("ABCD"): V_L1, V_L2, V_L3, I_L1, I_L2, I_L3, P_total y Energía_total_kWh.

Cabecera del Dragino

Los 2 primeros bytes del payload suelen ser de estado/batería, no de datos Modbus — por eso el offset inicial es 2, no 0.

Ajusta antes de producción

La dirección de registro Modbus varía entre SDM630 / SDM630-MCT / SDM630M según firmware: verifica siempre el datasheet real de tu contador.

codecs/sdm630_codec.js
// bytes: array de 4 elementos, orden ABCD (big-endian), típico en SDM630
function bytesToFloat32(bytes) {
  var buffer = new ArrayBuffer(4);
  var view = new DataView(buffer);
  for (var i = 0; i < 4; i++) view.setUint8(i, bytes[i]);
  return view.getFloat32(0, false); // false = big-endian
}

function decodeUplink(input) {
  var bytes = input.bytes;
  var data = {}, errors = [];
  var offset = 2; // 2 bytes de cabecera (battery/status) del Dragino

  var campos = [
    "voltaje_l1", "voltaje_l2", "voltaje_l3",
    "corriente_l1", "corriente_l2", "corriente_l3",
    "potencia_total_w", "energia_total_kwh"
  ];

  try {
    for (var i = 0; i < campos.length; i++) {
      var chunk = bytes.slice(offset, offset + 4);
      if (chunk.length < 4) { errors.push("Payload corto en " + campos[i]); break; }
      data[campos[i]] = Math.round(bytesToFloat32(chunk) * 100) / 100;
      offset += 4;
    }
  } catch (e) { errors.push("Error decodificando: " + e.message); }

  return { data: data, errors: errors };
}

Antes de darlo por bueno: contrasta el orden exacto de bytes con la consola serie del Dragino (o su app de configuración) y con el repositorio oficial dragino-end-node-decoder en GitHub, que mantiene ejemplos actualizados por modelo.

08 · Seguridad

Qué cambiar antes de pasar de laboratorio a producción.

La configuración de partida prioriza arrancar rápido para pruebas, no un despliegue expuesto a Internet. Antes de dar el stack por operativo con datos reales de clientes, repasa esto:

Mosquitto sin autenticación

Por defecto usa allow_anonymous true para simplificar el arranque. Crea usuarios con mosquitto_passwd y desactiva el acceso anónimo antes de producción.

Contraseñas y secretos por defecto

Cambia el admin/admin de ChirpStack, el sysadmin de ThingsBoard, el secret de la API de ChirpStack y la password de Postgres del .env.

Reverse proxy con TLS

Si el servidor es accesible desde Internet, pon Caddy o Traefik delante de los puertos web 8080/8081/1880 — nunca expongas esos puertos HTTP directamente.

Restringe el puerto del gateway

Limita 1700/udp (gateway-bridge) solo a las IPs de tus gateways físicos si tu red lo permite, para reducir la superficie expuesta.

09 · Datos del proyecto

¿Para quién y por quién se realiza esta oferta?

Estos datos aparecen en la portada y encabezados del informe (sección 11). Se guardan en este navegador y se comparten con el resto de herramientas del blog (localStorage), igual que en las demás calculadoras de Vatia&Co.

Proyecto / instalación
Técnico / autor de la oferta
Organización que presenta la oferta
Titular / cliente

Estos mismos campos (con id que empieza por "p_") aparecen ya rellenos si los usaste antes en otra calculadora del blog, y viceversa.

10 · Presupuesto

Oferta económica: hardware, instalación y mantenimiento.

Partidas orientativas para un despliegue de este tipo (precios de referencia, editables uno a uno). Indica el número de contadores y de gateways y pulsa "Aplicar cantidades" para rellenar automáticamente las partidas que dependen de ellas — el resto de cantidades y precios se pueden ajustar libremente a mano.

Sugerencia de gateways: 1 por cada ~40 contadores en un radio de hasta 5 km con línea de vista razonable; ajusta según tu estudio de cobertura real.

Periodo de garantía: 36 meses de compromiso mínimo, ampliable hasta 120 meses (10 años) — orientado a la vida útil que suelen asignarse a las medidas de monitorización y control energético en el sistema de Certificados de Ahorro Energético (CAE), donde mantener la supervisión activa durante ese periodo es lo que permite acreditar el ahorro. Verifica la vida útil exacta en la ficha CAE vigente si el proyecto se va a presentar a certificación.

Hardware
ConceptoCantidadPrecio unitario (€)Subtotal
Subtotal hardware—
Instalación y puesta en marcha
ConceptoCantidadPrecio unitario (€)Subtotal
Subtotal instalación y puesta en marcha—
Mantenimiento durante el periodo de garantía (cuota mensual, obligatoria)
ConceptoCantidadPrecio unitario (€/mes)Subtotal
Subtotal mantenimiento mensual—

Durante el periodo de garantía (mínimo 36 meses, arriba) el mantenimiento es obligatorio y cubre actualizaciones del stack, copias de seguridad, soporte técnico y sustitución de hardware defectuoso — se contrata por todo el periodo, no mes a mes. Si el proyecto se acoge a un CAE, esta continuidad de la supervisión es además la que justifica el ahorro acreditado durante la vida útil de la medida.

Nota comercial: en este tipo de proyecto el hardware y la instalación suelen cotizarse muy ajustados frente a la competencia; el margen se concentra en esta cuota de mantenimiento, de coste relativamente alto y obligada durante toda la garantía — ajusta los precios de esta tabla en consecuencia antes de enviar la oferta.

Resumen de la oferta
11 · Informe final

Oferta económica generada a partir de tus datos.

Estructura orientativa: portada con datos del proyecto y cliente, alcance del stack, partidas de hardware/instalación/mantenimiento con sus importes, resumen económico y condiciones. Rellena las secciones 09 y 10 antes de generar el informe.

Pulsa "Generar / actualizar vista previa" para componer el documento con los datos introducidos, y "Descargar oferta en PDF" para obtener el informe descargable.